import { Component } from './Component';
import { EventEmitter } from '../utils/EventEmitter';
import { Vector2 } from '../physics/Vector2';
import { Transform } from '../components/Transform';

export interface GameObjectOptions {
    name?: string;
    x?: number;
    y?: number;
    width?: number;
    height?: number;
    rotation?: number;
    visible?: boolean;
    active?: boolean;
}

export class GameObject extends EventEmitter {
    private static nextId = 0;
    
    public readonly id: number;
    public name: string;
    public transform: Transform;
    public visible: boolean;
    public active: boolean;
    
    private components: Map<string, Component>;
    private children: Set<GameObject>;
    protected parent: GameObject | null;
    
    constructor(options: GameObjectOptions = {}) {
        super();
        
        this.id = GameObject.nextId++;
        this.name = options.name || `GameObject_${this.id}`;
        this.visible = options.visible ?? true;
        this.active = options.active ?? true;
        
        // Initialize transform component
        this.transform = new Transform(this);
        this.transform.position = new Vector2(options.x || 0, options.y || 0);
        this.transform.rotation = options.rotation || 0;
        this.transform.scale = new Vector2(1, 1);
        
        this.components = new Map();
        this.children = new Set();
        this.parent = null;
    }
    
    public getParent(): GameObject | null {
        return this.parent;
    }
    
    public addComponent<T extends Component>(component: T): T {
        const componentName = component.constructor.name;
        if (this.components.has(componentName)) {
            throw new Error(`Component ${componentName} already exists on GameObject ${this.name}`);
        }
        
        this.components.set(componentName, component);
        component.gameObject = this;
        component.onAttach();
        
        return component;
    }
    
    public getComponent<T extends Component>(componentType: new (...args: any[]) => T): T | null {
        return (this.components.get(componentType.name) as T) || null;
    }
    
    public removeComponent<T extends Component>(componentType: new (...args: any[]) => T): void {
        const component = this.components.get(componentType.name);
        if (component) {
            component.onDetach();
            this.components.delete(componentType.name);
        }
    }
    
    public addChild(child: GameObject): void {
        if (child.parent) {
            child.parent.removeChild(child);
        }
        child.parent = this;
        this.children.add(child);
        this.emit('childAdded', child);
    }
    
    public removeChild(child: GameObject): void {
        if (this.children.has(child)) {
            this.children.delete(child);
            child.parent = null;
            this.emit('childRemoved', child);
        }
    }
    
    public getChildren(): GameObject[] {
        return Array.from(this.children);
    }
    
    public update(deltaTime: number): void {
        if (!this.active) return;
        
        // Update components
        for (const component of this.components.values()) {
            if (component.active) {
                component.update(deltaTime);
            }
        }
        
        // Update children
        for (const child of this.children) {
            child.update(deltaTime);
        }
        
        this.emit('update', deltaTime);
    }
    
    public render(ctx: CanvasRenderingContext2D): void {
        if (!this.visible || !this.active) return;
        
        ctx.save();
        
        // Apply transform
        const pos = this.transform.position;
        const scale = this.transform.scale;
        const rotation = this.transform.rotation;
        
        ctx.translate(pos.x, pos.y);
        ctx.rotate(rotation);
        ctx.scale(scale.x, scale.y);
        
        // Render components
        for (const component of this.components.values()) {
            if (component.active) {
                component.render(ctx);
            }
        }
        
        // Render children
        for (const child of this.children) {
            child.render(ctx);
        }
        
        ctx.restore();
        
        this.emit('render', ctx);
    }
    
    public destroy(): void {
        // Destroy components
        for (const component of this.components.values()) {
            component.onDetach();
        }
        this.components.clear();
        
        // Destroy children
        for (const child of this.children) {
            child.destroy();
        }
        this.children.clear();
        
        // Remove from parent
        if (this.parent) {
            this.parent.removeChild(this);
        }
        
        this.emit('destroy');
        this.removeAllListeners();
    }
} 